package com.engine.salary.entity.salaryacct.bo;

import com.cloudstore.eccom.pc.table.WeaTableOperate;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryBill.dto.SalarySendCheckDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctEmployeeCountDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctRecordListDTO;
import com.engine.salary.entity.salaryacct.param.SalaryAcctRecordSaveParam;
import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO;
import com.engine.salary.entity.salarysob.dto.SalarySobCycleDTO;
import com.engine.salary.entity.salarysob.po.SalarySobPO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.salaryaccounting.SalaryAcctRecordStatusEnum;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 薪资核算
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class SalaryAcctRecordBO {

    /**
     * 将薪资核算记录po转换成薪资核算记录列表dto
     *
     * @param salaryAcctRecordPOS
     * @param salarySobs
     * @param employeeComInfos
     * @param salaryAcctEmployeeCountDTOS
     * @return
     */
    public static List<SalaryAcctRecordListDTO> convert2ListDTO(List<SalaryAcctRecordPO> salaryAcctRecordPOS,
                                                                List<SalarySobPO> salarySobs,
                                                                List<DataCollectionEmployee> employeeComInfos,
                                                                List<SalaryAcctEmployeeCountDTO> salaryAcctEmployeeCountDTOS,
                                                                List<SalarySendCheckDTO> salarySendCheckResult,
                                                                List<TaxAgentPO> taxAgentPOS,
                                                                List<Long> needApprovalSalarySobIds,
                                                                boolean approvalCanFile,
                                                                boolean approvalCanReCalc) {
        if (CollectionUtils.isEmpty(salaryAcctRecordPOS)) {
            return Collections.emptyList();
        }
        Map<Long, SalarySobPO> salarySobMap = SalaryEntityUtil.convert2Map(salarySobs, SalarySobPO::getId);
        Map<Long, String> usernameMap = SalaryEntityUtil.convert2Map(employeeComInfos, DataCollectionEmployee::getEmployeeId, DataCollectionEmployee::getUsername);
        Map<Long, Long> empSizeMap = SalaryEntityUtil.convert2Map(salaryAcctEmployeeCountDTOS, SalaryAcctEmployeeCountDTO::getSalaryAcctRecordId, SalaryAcctEmployeeCountDTO::getCountBySalaryAcctRecordId);
        Map<Long, Boolean> salarySendMap = SalaryEntityUtil.convert2Map(salarySendCheckResult, SalarySendCheckDTO::getSalaryAcctId, SalarySendCheckDTO::isSendFinished);
        Map<Long, String> taxAgentMap = SalaryEntityUtil.convert2Map(taxAgentPOS, TaxAgentPO::getId, TaxAgentPO::getName);
        return salaryAcctRecordPOS.stream()
                .map(salaryAcctRecordPO -> {
                    SalarySobPO salarySobPO = salarySobMap.get(salaryAcctRecordPO.getSalarySobId());
                    SalaryAcctRecordStatusEnum salaryAcctRecordStatusEnum = SalaryAcctRecordStatusEnum.parseByValue(salaryAcctRecordPO.getStatus());
                    // 只有未归档时可以"核算"、"删除"、"归档"
                    // 归档后、工资单全部发放以后可以"查看"、"重新核算"、"回算"
                    // 归档后、申报后可以"查看"、"重新核算"
                    List<WeaTableOperate> btnList = new ArrayList<>();
                    if (SalaryAcctRecordStatusEnum.NOT_ARCHIVED == salaryAcctRecordStatusEnum) {
                        btnList.add(new WeaTableOperate("核算", null, "0"));
                        if(!Objects.equals(salaryAcctRecordPO.getBackCalcStatus(),NumberUtils.INTEGER_ONE)){
                            btnList.add(new WeaTableOperate("删除", null, "1"));
                        }
                        if (approvalCanFile || !needApprovalSalarySobIds.contains(salaryAcctRecordPO.getSalarySobId())) {
                            btnList.add(new WeaTableOperate("归档", null, "2"));
                        }
                    } else if (SalaryAcctRecordStatusEnum.ARCHIVED == salaryAcctRecordStatusEnum &&  ( salarySendMap.get(salaryAcctRecordPO.getId()) ==Boolean.TRUE  ) ){
                        btnList.add(new WeaTableOperate("查看", null, "3"));
                        if (approvalCanReCalc || !needApprovalSalarySobIds.contains(salaryAcctRecordPO.getSalarySobId())) {
                            btnList.add(new WeaTableOperate("重新核算", null, "4"));
                        }
                        if(salaryAcctRecordPO.getBackCalcStatus() == null || salaryAcctRecordPO.getBackCalcStatus() == NumberUtils.INTEGER_ZERO) {
                            btnList.add(new WeaTableOperate("回算", null, "5"));
                        }
                    } else {
                        btnList.add(new WeaTableOperate("查看", null, "3"));
                        if (approvalCanReCalc || !needApprovalSalarySobIds.contains(salaryAcctRecordPO.getSalarySobId())) {
                            btnList.add(new WeaTableOperate("重新核算", null, "4"));
                        }
                    }
                    String taxAgentName = salarySobPO.getTaxAgentIds().stream().map(id -> taxAgentMap.getOrDefault(id, "")).collect(Collectors.joining(","));
                    return SalaryAcctRecordListDTO.builder()
                            .id(salaryAcctRecordPO.getId())
                            .salarySobName(Optional.ofNullable(salarySobPO).map(SalarySobPO::getName).orElse(StringUtils.EMPTY))
                            .taxAgentName(taxAgentName)
                            .salaryMonth(SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth()).toString())
                            .taxCycle(SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getTaxCycle()).toString())
                            .status(Optional.ofNullable(salaryAcctRecordStatusEnum)
                                    .map(o -> SalaryI18nUtil.getI18nLabel(o.getLabelId(), o.getDefaultLabel()))
                                    .orElse(StringUtils.EMPTY))
                            .acctTimes(salaryAcctRecordPO.getAcctTimes())
                            .acctTimesViable(salaryAcctRecordPO.getAcctTimes() >= 1)
                            .backCalcStatus(salaryAcctRecordPO.getBackCalcStatus() == null ? 0 : salaryAcctRecordPO.getBackCalcStatus())
                            .employeeSize(empSizeMap.getOrDefault(salaryAcctRecordPO.getId(), NumberUtils.LONG_ZERO))
                            .accountantName(usernameMap.getOrDefault(salaryAcctRecordPO.getCreator(), StringUtils.EMPTY))
                            .updateTime(SalaryDateUtil.getFormatLocalDateTime(salaryAcctRecordPO.getUpdateTime()))
                            .description(salaryAcctRecordPO.getDescription())
                            .approvalStatus(salaryAcctRecordPO.getApprovalStatus())
                            .operate(btnList)
                            .opts(salaryAcctRecordPO.getOpts())
                            .build();
                }).collect(Collectors.toList());
    }

    /**
     * 将薪资核算保存参数转换成薪资核算记录po
     *
     * @param saveParam
     * @param salarySobCycle
     * @param acctTimes
     * @param employeeId
     * @return
     */
    public static SalaryAcctRecordPO convert2PO(SalaryAcctRecordSaveParam saveParam,
                                                SalarySobCycleDTO salarySobCycle,
                                                int acctTimes, Long employeeId) {
        Date now = new Date();
        return new SalaryAcctRecordPO()
                .setSalaryMonth(SalaryDateUtil.localDateToDate(saveParam.getSalaryMonth().atDay(1)))
                .setTaxCycle(SalaryDateUtil.localDateToDate(salarySobCycle.getTaxCycle().atDay(1)))
                .setSalarySobId(saveParam.getSalarySobId())
                .setStatus(SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue())
                .setAcctTimes(acctTimes + 1)
                .setBackCalcStatus(0)
                .setDescription(saveParam.getDescription())
                .setDeleteType(NumberUtils.INTEGER_ZERO)
                .setCreator(employeeId)
                .setCreateTime(now)
                .setUpdateTime(now)
                .setTenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY);
    }

    /**
     * 重新计算薪资核算记录的核算次数
     *
     * @param deletedSalaryAcctRecordPOS 已经删除的薪资核算记录
     * @param toUpdateAcctTimesRecordPOS 待更新核算次数的薪资核算记录
     * @return
     */
    public static List<SalaryAcctRecordPO> updateAcctTimes(List<SalaryAcctRecordPO> deletedSalaryAcctRecordPOS,
                                                           List<SalaryAcctRecordPO> toUpdateAcctTimesRecordPOS) {
        if (CollectionUtils.isEmpty(toUpdateAcctTimesRecordPOS)) {
            return Collections.emptyList();
        }
        Date now = new Date();
        List<SalaryAcctRecordPO> resultList = Lists.newArrayListWithExpectedSize(toUpdateAcctTimesRecordPOS.size());
        // 已经删除的薪资核算记录id
        Set<Long> deletedSalaryAcctRecordIds = SalaryEntityUtil.properties(deletedSalaryAcctRecordPOS, SalaryAcctRecordPO::getId);
        // 已经删除的薪资核算记录组成的key集合（薪资账套id+薪资所属月）
        Set<String> deletedKeySet = SalaryEntityUtil.properties(deletedSalaryAcctRecordPOS, salaryAcctRecordPO ->
                salaryAcctRecordPO.getSalarySobId() + SalaryDateUtil.getFormatYearMonth(salaryAcctRecordPO.getSalaryMonth()));
        // 按照（薪资账套id+薪资所属月）作为key聚合而成的待更新核算次数的薪资核算记录map
        Map<String, List<SalaryAcctRecordPO>> toUpdateAcctTimesRecordPOMap = SalaryEntityUtil.group2Map(toUpdateAcctTimesRecordPOS, salaryAcctRecordPO ->
                salaryAcctRecordPO.getSalarySobId() + SalaryDateUtil.getFormatYearMonth(salaryAcctRecordPO.getSalaryMonth()));
        for (String key : deletedKeySet) {
            List<SalaryAcctRecordPO> salaryAcctRecordPOS = toUpdateAcctTimesRecordPOMap.getOrDefault(key, Collections.emptyList())
                    .stream()
                    .filter(salaryAcctRecordPO -> !deletedSalaryAcctRecordIds.contains(salaryAcctRecordPO.getId()))
                    .sorted(Comparator.comparingLong(salaryAcctRecordPO -> SalaryDateUtil.localDateTime2EpochMilli(salaryAcctRecordPO.getCreateTime())))
                    .collect(Collectors.toList());
            int acctTimes = 1;
            for (SalaryAcctRecordPO salaryAcctRecordPO : salaryAcctRecordPOS) {
                salaryAcctRecordPO.setAcctTimes(acctTimes++);
                salaryAcctRecordPO.setUpdateTime(now);
                resultList.add(salaryAcctRecordPO);
            }
        }
        return resultList;
    }
}
